﻿using System;
using System.Collections.Generic;
using MoonSharp.Interpreter.Diagnostics;
using System.Linq;
using System.Text;
using MoonSharp.Interpreter.Execution;
using NUnit.Framework;

namespace MoonSharp.Interpreter.Tests
{
	/// <summary>
	/// Selected tests extracted from Lua test suite
	/// </summary>
	[TestFixture]
	class LuaTestSuiteExtract
	{
		void RunTest(string script)
		{
			HashSet<int> failedTests = new HashSet<int>();
			int i = 0;

			var globalCtx = new Table();
			globalCtx[new RValue("assert")] = new RValue(new CallbackFunction(
				a => 
				{
					++i;

					if (!a[0].TestAsBoolean())
						failedTests.Add(i);

					return RValue.Nil; 
				}));

			globalCtx[new RValue("print")] = new RValue(new CallbackFunction(a =>
				{
					// Debug.WriteLine(string.Join(" ", a.Select(v => v.AsString()).ToArray()));
					return RValue.Nil;
				}));

			RValue res = MoonSharpInterpreter.LoadFromString(script, globalCtx).Execute();

			Assert.IsFalse(failedTests.Any(), string.Format("Failed asserts {0}",
				string.Join(", ", failedTests.Select(xi => xi.ToString()).ToArray())));
		}

		[Test][Ignore("VM Transition")]
		public void LuaSuite_Calls_LocalFunctionRecursion()
		{
			RunTest(@"
				-- testing local-function recursion
				fact = false
				do
				  local res = 1
				  local function fact (n)
					if n==0 then return res
					else return n*fact(n-1)
					end
				  end
				  assert(fact(5) == 120)
				end
				assert(fact == false)
				");
		}

		[Test][Ignore("VM Transition")]
		public void LuaSuite_Calls_Declarations()
		{
			RunTest(@"
				-- testing local-function recursion
				-- testing declarations
				a = {i = 10}
				self = 20
				function a:x (x) return x+self.i end
				function a.y (x) return x+self end

				assert(a:x(1)+10 == a.y(1))

				a.t = {i=-100}
				a['t'].x = function (self, a,b) return self.i+a+b end

				assert(a.t:x(2,3) == -95)

				do
				  local a = {x=0}
				  function a:add (x) self.x, a.y = self.x+x, 20; return self end
				  assert(a:add(10):add(20):add(30).x == 60 and a.y == 20)
				end

				local a = {b={c={}}}

				function a.b.c.f1 (x) return x+1 end
				function a.b.c:f2 (x,y) self[x] = y end
				assert(a.b.c.f1(4) == 5)
				a.b.c:f2('k', 12); assert(a.b.c.k == 12)

				print('+')

				t = nil   -- 'declare' t
				function f(a,b,c) local d = 'a'; t={a,b,c,d} end

				f(      -- this line change must be valid
				  1,2)
				assert(t[1] == 1 and t[2] == 2 and t[3] == nil and t[4] == 'a')
				f(1,2,   -- this one too
					  3,4)
				assert(t[1] == 1 and t[2] == 2 and t[3] == 3 and t[4] == 'a')

				");
		}

		[Test][Ignore("VM Transition")]
		public void LuaSuite_Calls_Closures()
		{
			RunTest(@"
				-- fixed-point operator
				Z = function (le)
					  local function a (f)
						return le(function (x) return f(f)(x) end)
					  end
					  return a(a)
					end


				-- non-recursive factorial

				F = function (f)
					  return function (n)
							   if n == 0 then return 1
							   else return n*f(n-1) end
							 end
					end

				fat = Z(F)

				assert(fat(0) == 1 and fat(4) == 24 and Z(F)(5)==5*Z(F)(4))

				local function g (z)
				  local function f (a,b,c,d)
					return function (x,y) return a+b+c+d+a+x+y+z end
				  end
				  return f(z,z+1,z+2,z+3)
				end

				f = g(10)

				assert(f(9, 16) == 10+11+12+13+10+9+16+10)

				Z, F, f = nil
				print('+')
				");
		}





	}
}
